package cn.xinfei.xdecision.engine.runner.executor.strategy;

import cn.xinfei.xdecision.common.model.component.strategyout.StrategyOutput;
import cn.xinfei.xdecision.common.service.component.strategyout.StrategyOutputService;
import cn.xinfei.xdecision.common.utils.enums.enginex.NodeTypeEnum;
import cn.xinfei.xdecision.common.utils.util.JsonUtil;
import cn.xinfei.xdecision.common.vo.FieldInfoVo;
import cn.xinfei.xdecision.engine.runner.context.PipelineContextHolder;
import cn.xinfei.xdecision.engine.runner.datax.DataRealCollectHandler;
import cn.xinfei.xdecision.engine.runner.executor.AbsNodeHandler;
import cn.xinfei.xdecision.engine.runner.executor.handler.NodeOutputHandler;
import cn.xinfei.xdecision.engine.runner.metadata.MetadataProvider;
import com.dianping.cat.Cat;
import io.micrometer.core.instrument.MeterRegistry;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Data
public abstract class EngineRunner<T extends AbsNodeHandler> implements IEngineRunner<T> {

    @Autowired
    protected StrategyOutputService strategyOutputService;

    @Autowired
    protected DataRealCollectHandler dataRealCollectHandler;

    @Autowired
    protected MetadataProvider metadataProvider;

    @Autowired
    private NodeOutputHandler nodeOutputHandler;

    @Autowired
    private MeterRegistry meterRegistry;


    /**
     * 只从上下文获取
     *
     * @param nodeHandler
     * @return
     */
    @Override
    public Map<String, Object> getNodeInput(T nodeHandler) {
        try {
            Map<String, Object> contextValue = PipelineContextHolder.getContextValue();
            String nodeType = nodeHandler.getNodeType();
            if (NodeTypeEnum.CHILD_ENGINE.getType().equals(nodeType)) {
                //子决策流的树入，就是上个决策流的全部上下文
                return contextValue;
            }

            if (NodeTypeEnum.START.getType().equals(nodeType)) {
                Map<String, Object> in = PipelineContextHolder.getIn();
                return in;
            }
            Map<String, FieldInfoVo> inputFields = nodeHandler.getInputFields();
            if (null != inputFields) {
                //此处的实现太过于简单，暂不支持复杂嵌套变量
                //标准实现参考CustomerOutputHandler：ExecuteUtils.getObjFromMap(contextValue, new String[]{fieldValue})

                Map<String, Object> input = new HashMap<>();
                inputFields.forEach((key, value) -> {
                    String fieldCode = key;
//                    Object fieldValue = AmmunitionDepot.getValueByKeyFromContext(ParamTypeConst.VARIABLE, fieldCode, fieldCode );
                    Object fieldValue = PipelineContextHolder.getValueByKey(fieldCode);
                    if (fieldValue != null) {
                        input.put(fieldCode, fieldValue);
                    }
                });

                return input;
            }
        } catch (Exception e) {
            log.error("setNodeInput fail.error={},", e.getMessage(), e);
            Cat.logError(e);
            meterRegistry.counter("xengine_node_input_error_total").increment();
        }
        return Collections.emptyMap();
    }

    /**
     * 记录命中终止条件后的输出，该输出被认为是决策结果输出
     *
     * @param nodeHandler
     * @param key
     * @param result
     */
    @Override
    public void setOutput(T nodeHandler, String key, Object result) {
        try {
            String nodeCode = nodeHandler.getCode();
            PipelineContextHolder.putInContext(nodeCode, key, result);
        } catch (Exception e) {
            log.error("recordResponse fail.error={},key={}, result={}", e.getMessage(), key, JsonUtil.toJson(result), e);
        }
    }

    /**
     * 支持从远程获取
     *
     * @param nodeHandler
     * @return
     */
    @Override
    public Map<String, Object> setNodeOutput(T nodeHandler) {
        Map<String, Object> customOutput = new HashMap<>();
        try {
            Map<String, StrategyOutput> outputFields = nodeHandler.getOutputFields();
            if (null != outputFields) {
                String nodeCode = nodeHandler.getCode();
                List<StrategyOutput> strategyOutputs = outputFields.entrySet().stream().map(e -> e.getValue()).collect(Collectors.toList());
                customOutput = nodeOutputHandler.setOutput(nodeCode, strategyOutputs);
            }
        } catch (Exception e) {
            log.error("setNodeOutput fail.error={},", e.getMessage(), e);
            Cat.logError(e);
            meterRegistry.counter("xengine_node_output_error_total").increment();
        }
        return customOutput;
    }

    /**
     * get 只从上下文获取
     *
     * @param nodeHandler
     * @return
     */
    @Override
    public Map<String, Object> getNodeOutput(T nodeHandler) {
        Map<String, Object> customOutput = new HashMap<>();
        try {
            String nodeCode = nodeHandler.getCode();
            Map<String, StrategyOutput> outputFields = nodeHandler.getOutputFields();
            if (null != outputFields) {
                List<StrategyOutput> strategyOutputs = outputFields.entrySet().stream().map(e -> e.getValue()).collect(Collectors.toList());
                //这个输出只能取上下文里取值
//                Map<String, Object> customOutput = nodeOutputHandler.executeOutputInContext(nodeCode, strategyOutputs);
                customOutput = nodeOutputHandler.getOutput(nodeCode, strategyOutputs);
            }

            Map<String, StrategyOutput> terminalOutputFields = nodeHandler.getTerminalOutputFields();
            if (null != terminalOutputFields) {
                List<StrategyOutput> strategyOutputs = terminalOutputFields.entrySet().stream().map(e -> e.getValue()).collect(Collectors.toList());
                customOutput.putAll(nodeOutputHandler.getOutput(nodeCode, strategyOutputs));
            }

            Map<String, Object> contextValue = PipelineContextHolder.getContextValue();
            String nodeType = nodeHandler.getNodeType();
            if (NodeTypeEnum.CHILD_ENGINE.getType().equals(nodeType)) {
                //子决策流的输出，就是当前决策流的全部上下文
                return contextValue;
            }
            if (NodeTypeEnum.END.getType().equals(nodeType)) {
                Map<String, Object> out = PipelineContextHolder.getOut();
                return out;
            }
            if (NodeTypeEnum.RULE_BASE.getType().equals(nodeType)) {
                //规则节点比较特殊，单独处理，他的输出，取决于block块命中的情况. 此处不做通用的处理逻辑
                return new HashMap<>();
            }

        } catch (Exception e) {
            log.error("getNodeOutput fail.error={},", e.getMessage(), e);
            Cat.logError(e);
            meterRegistry.counter("xengine_node_output_error_total").increment();
        }
        return customOutput;
    }
}
